-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[libc++] std::cmp_less and other integer comparison functions could be improved #151332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[libc++] std::cmp_less and other integer comparison functions could be improved #151332
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-libcxx Author: None (kisuhorikka) ChangesFix #65136 Full diff: https://github.com/llvm/llvm-project/pull/151332.diff 1 Files Affected:
diff --git a/libcxx/include/__utility/cmp.h b/libcxx/include/__utility/cmp.h
index 14dc0c154c040..4c29f09628095 100644
--- a/libcxx/include/__utility/cmp.h
+++ b/libcxx/include/__utility/cmp.h
@@ -28,7 +28,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_equal(_Tp __t, _Up __u) noexcept {
- if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
+ if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
+ __builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
+ return static_cast<int>(__t) == static_cast<int>(__u);
+ } else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
+ __builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
+ return static_cast<long long>(__t) == static_cast<long long>(__u);
+ } else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
return __t == __u;
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? false : make_unsigned_t<_Tp>(__t) == __u;
@@ -43,7 +49,13 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool cmp_not_equal(_Tp __t, _Up __u) noexcept {
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_less(_Tp __t, _Up __u) noexcept {
- if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
+ if constexpr (sizeof(_Tp) < sizeof(int) && sizeof(_Up) < sizeof(int)) {
+ __builtin_assume(__t < numeric_limits<int>::max() && __u < numeric_limits<int>::max());
+ return static_cast<int>(__t) < static_cast<int>(__u);
+ } else if constexpr (sizeof(_Tp) < sizeof(long long) && sizeof(_Up) < sizeof(long long)) {
+ __builtin_assume(__t < numeric_limits<long long>::max() && __u < numeric_limits<long long>::max());
+ return static_cast<long long>(__t) < static_cast<long long>(__u);
+ } else if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
return __t < __u;
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? true : make_unsigned_t<_Tp>(__t) < __u;
|
libcxx seems unhappy with |
Have you read this discussion: #91612 |
cedea5b
to
b2409ee
Compare
Checks for last commit failed due to 'interrupted by user', and I found no obvious errors. The discussion points out that the CI is not done running yet and that job needs to be restarted (which will happen automatically). So I will try to commit again if the CI doesn't restarts automatically after hours. |
libcxx/include/__utility/cmp.h
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you add these in the first place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previous bench shows slower results when converting parameters from short
to long long
. The performance is better if parameters are converted to int
. https://quick-bench.com/q/q87HT8ePhR1AqI_Gy3Zj6rbElp8
However, the above results are based on clang 15 (I neglected this before unfortunately), and the difference seems vanished on clang 17. https://quick-bench.com/q/q0Q-bgFsVQyqe8QiDTiPHkawIu4
Though the little difference between speed, the assembly for int
is shorter than long long
, and _LIBCPP_ASSUME
seems no work now. https://godbolt.org/z/a5s3zx64W
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Clang 21 (or 22?), _LIBCPP_ASSUME
doesn't affect the generated same assembly. I'd like to remove _LIBCPP_ASSUME
here.
Issues like that can happen. Just restart the failed job if you have access or rebase to restart the CI. Sometimes somebody can restart the job for you. Specifically I have the same issue with buildkite/libcxx-ci/aarch64 |
680faaa
to
00b0d72
Compare
Hi @philnik777 There are 3 CI tests waiting for status to be reported for 3 days. The CI might be required to start manually for first PR in similar case . Could you please help to start it? Thank you for your help! |
libcxx/include/__utility/cmp.h
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't cover comparison between int
and unsigned short
yet.
I wonder whether we can/should do the following (ditto cmp_less
).
template <__signed_or_unsigned_integer _Tp, __signed_integer _Ip>
inline constexpr bool __comparison_can_promote_to =
__signed_integer<_Tp> ? sizeof(_Tp) <= sizeof(_Ip) : sizeof(_Tp) < sizeof(_Ip);
template <__signed_or_unsigned_integer _Tp, __signed_or_unsigned_integer _Up>
_LIBCPP_HIDE_FROM_ABI constexpr bool cmp_equal(_Tp __t, _Up __u) noexcept {
if constexpr (is_signed_v<_Tp> == is_signed_v<_Up>)
return __t == __u;
else if constexpr (__comparison_can_promote_to<_Tp, int> && __comparison_can_promote_to<_Up, int>)
return static_cast<int>(__t) == static_cast<int>(__u);
else if constexpr (__comparison_can_promote_to<_Tp, long long> && __comparison_can_promote_to<_Up, long long>)
return static_cast<long long>(__t) == static_cast<long long>(__u);
else if constexpr (is_signed_v<_Tp>)
return __t < 0 ? false : make_unsigned_t<_Tp>(__t) == __u;
else
return __u < 0 ? false : __t == make_unsigned_t<_Up>(__u);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This starts to feel like it should be a compiler builtin.
534afd4
to
f39c2b6
Compare
f39c2b6
to
f421f9f
Compare
f421f9f
to
a888172
Compare
ping |
ping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we get some benchmarks for this change in libcxx/test/benchmarks/
?
a888172
to
09796b7
Compare
Original benchmarks like |
09796b7
to
b51c731
Compare
79bed33
to
1acfba7
Compare
That's a conformance test, not a benchmark. |
1acfba7
to
1d74e7a
Compare
Then you can post the benchmark results in the description of this PR, which will be generally used as the squashed commit message. |
1d74e7a
to
631b182
Compare
✅ With the latest revision this PR passed the C/C++ code formatter. |
631b182
to
e31deed
Compare
Benchmark Previous:
After:
|
452ecc7
to
c6aec70
Compare
c6aec70
to
4afea89
Compare
Can we show that the regression for the |
There are only few instructions for each comparison whose benchmarks might be disturbed by cache misses. So more test cases are added in the new commit to average the impact of cache misses. Updated benchmark:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with PR message updated to mention new benchmark results (before/after).
Could you provide a table with the relative speedups? You should be able to use |
|
@kisuhorikka Congratulations on having your first Pull Request (PR) merged into the LLVM Project! Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR. Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues. How to do this, and the rest of the post-merge process, is covered in detail here. If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again. If you don't get any reports, no action is required from you. Your changes are working as expected, well done! |
@frederick-vs-ja When we merge commits like this, let's try not to include full benchmark output in the commit message, or if we do, try to make it aligned properly. The commit message ended up being very long and difficult to read because the table got mis-aligned by Github upon squashing. |
Fix #65136